Ubuntu 18.04LTS: Auto-Locking When YubiKey is Removed

It took me a while to piece this together, so I figured that I would write a handy “how-to”, in order to prevent someone else from losing the man-hours that it took to piecemeal this together.

The first thing that we’ll need to do is toย create the monitoring rule. This rule is specifically used to key-off of the event that is trigged when the YubiKey is removed; however, we’ll need some information for that before we can write the rule.

You can use the below command to obtain the device-specific information:

udevadm monitor --environment --udev

With the command running, remove the YubiKey device from the system. I would suggest copying all of the properties that you see in the output to a text file. We’ll use some of these to define your rule.

Right. So, let’s create the rule. Run this command to start the text editor:

sudo nano /etc/udev/rules.d/85-yubikey.rules

In the text editor, you’ll add the rule. Below is mine, for an example:

# Yubikey Udev Rule: running a bash script in case your Yubikey is removed 
ACTION=="remove", ENV{ID_VENDOR_ID}=="1050", ENV{ID_MODEL_ID}=="0401", RUN+="/usr/local/bin/gnome-screensaver-lock"

The most specific piece that you’ll copy, verbatum, is the overloaded “Run” section.

Press Ctrl+X in the text editor window, when you’re done. Press your key for confirmation (‘Y’ in English, ‘J’ in Swedish, etc.). Then press ‘Enter’ (a.k.a.: carriage return) to confirm the path we specified when we started the text editor.

You should now be back at the terminal.

Now that the rule is in place, we need to define the action. We’ve already declared where it will go to fetch the action definition (see the overloaded “Run” declaration above) and now we need to create that file.

Run this command to start the text editor:

sudo nano /usr/local/bin/gnome-screensaver-lock

Add the following code in the editor:

# Double checking if the Yubikey is actually removed
if [ -z "$(lsusb | grep Yubico)" ]
        logger "YubiKey Removed or Changed"
        sessionids=`/bin/loginctl list-sessions | grep <userAccount> | awk '{print $1}'`
        for id in $sessionids
                        logger "Locking session id:" $id
                        /bin/loginctl lock-session $id

(Be sure to change <userAccount> to your actual user account.)

Again, press Ctrl+X to start exiting the text editor. Press the confirmation key. Press the enter key to confirm the path.

The script that we just made must be made an executable for udev to be able to leverage it:

sudo chmod +x /usr/local/bin/gnome-screensaver-lock

Now that we have the rule and the actions defined, we need to tell the udevd service to reload the rules, so it’ll work. Run the following in the terminal:

sudo udevadm control --reload-rules
sudo service udev reload

Now, we should be able to remove the YubiKey device and Ubuntu should auto-lock.

If it doesn’t, you can try to monitor the udev service:

sudo service udev status
โ— systemd-udevd.service - udev Kernel Device Manager
   Loaded: loaded (/lib/systemd/system/systemd-udevd.service; static; vendor preset: enabled)
   Active: active (running) since Mon 2019-02-25 23:11:28 CET; 4 days ago
     Docs: man:systemd-udevd.service(8)
 Main PID: 482 (systemd-udevd)
   Status: "Processing with 24 children at max"
    Tasks: 1
   CGroup: /system.slice/systemd-udevd.service
           โ””โ”€482 /lib/systemd/systemd-udevd

mar 02 22:39:58 Tradgardsforeningen mtp-probe[15598]: checking bus 1, device 30: "/sys/devices/pci0000:00/0000:00:14.0/usb1/1-3"
mar 02 22:39:58 Tradgardsforeningen mtp-probe[15598]: bus: 1, device: 30 was not an MTP device
mar 02 22:43:17 Tradgardsforeningen systemd-udevd[15742]: Process '/usr/local/bin/gnome-screensaver-lock' failed with exit code 2.
mar 02 22:43:17 Tradgardsforeningen systemd-udevd[15749]: Process '/usr/local/bin/gnome-screensaver-lock' failed with exit code 2.
mar 02 22:43:20 Tradgardsforeningen mtp-probe[15759]: checking bus 1, device 31: "/sys/devices/pci0000:00/0000:00:14.0/usb1/1-3"
mar 02 22:43:20 Tradgardsforeningen mtp-probe[15759]: bus: 1, device: 31 was not an MTP device
mar 02 22:48:00 Tradgardsforeningen mtp-probe[15901]: checking bus 1, device 32: "/sys/devices/pci0000:00/0000:00:14.0/usb1/1-3"
mar 02 22:48:00 Tradgardsforeningen mtp-probe[15901]: bus: 1, device: 32 was not an MTP device
mar 02 22:49:26 Tradgardsforeningen mtp-probe[16019]: checking bus 1, device 33: "/sys/devices/pci0000:00/0000:00:14.0/usb1/1-3"
mar 02 22:49:26 Tradgardsforeningen mtp-probe[16019]: bus: 1, device: 33 was not an MTP device

The above snippet is from when the executing script looked quite different and, thus, it was failing to execute. Keep in mind that you may need to define different parameters in your monitoring rule, for example, if your device has a different vendor or model id and the removal isn’t creating the logging event:

mar 02 23:00:15 Tradgardsforeningen root[16941]: YubiKey Removed or Changed

If you can see the event being logged, then verify that you specified the correct username in the script.

If all else fails, there’s Ask Ubuntu. ๐Ÿ™‚

Thanks for coming to this TEDTalk and I hope that it helps prevent someone losing the hours that I lost. ๐Ÿ™‚

Wait-Chain Traversal: Or, How We Can Use PowerShell to JIT C# to Load an Unmanaged Assembly (C++), InterOping With Windows APIs, Returning that Data Back to PowerShell

So, as the long-winded title infers, today I’ll be covering something that I wrote a long time ago and have recently re-written (albeit, probably badly) for you to use and/or learn from.

In this case, we’re using a PowerShell script, which JITs C# code, the C# code calls into an unmanaged DLL (C++), and that calls into an Windows API. Once the data has been obtained from the Windows API, we pass the data back from the unmanaged assembly to the managed code (via Marshal) and then return that back to the PowerShell instance to be displayed to the user.

Before we dive into the what we’re doing, we should cover some key concepts. The first is JIT’ing. JIT stands for “Just-In-Time” (Compilation) and the name is slightly a misnomer but we’ll cover that in a second. So, in JIT, what happens is that the run time precompiles the code before it’s ran. This is important because a key-concept in exception handling is the runtime’s seek operation to find a handler for an exception that is thrown. You’ll often see this as a FirstChanceException in a dump file. In PowerShell, we have the added ability to leverage the JIT compilation by passing source code as a type into the App’s Domain. It’s important to distinguish that once the App Domain has been disposed of, the type specified is lost and has to be re-instantiated again.

So, what – exactly – is this code going to be doing? Well, since Windows Vista, the Windows Operating System exposes the Wait Chain Traversal API. You can see a demonstration of this API in Task Manager: Go to the Details tab, right click on a process and click “Analyze Wait Chain”.

Since Windows Server 2016 Core doesn’t include a desktop or any GUI interfaces, a more robust way was needed to obtain the same information in production, to determine if the reason an application wasn’t responding or performing work was because the threads were blocked.

When you run the code, you can tell if this is the case or not by something like the following:

12972ย ย  [7464:12972:blocked]->[ThreadWait]->[7464:12968:blocked]->[End]

Where the first thread is blocked by a thread wait on the the second thread, which is also (itself) blocked.

So, first things first, the PowerShell code. Take a peek here to see that. Note that the Source code is contained with a specific character-delimited string @”<code>”@. After that we add a Type, pointing it to the source code we’ve defined and referencing the assemblies that we’ll need for this type to work. Worthy of noting is that when we add this type, it is exposed in PowerShell the same way any normal .NET type is, via the []:: convention.

Note that in the C# source we import the unmanaged DLL and reference the exposed method. In the body of the code, we also construct an IntPtr to reference for the return. So, now, we get to Marshalling.

An IntPtr is, quite literally, a pointer or handle to an object. A pointer is a reference to memory where an object exists and the object is typically delimited by characters to signify the termination of it (e.g.: the end of a string is null-terminated). A handle is roughly the same premise but the handle abstracts memory management from the caller. So, at 200 ticks, the handle could point to address 0x000001 and at 369 ticks, it could point to 0x34778.

Alright, so why this matters is because when we pass from unmanaged code back to managed code, there’s nothing that implicitly tells the managed code where to find the object in native memory; so, we have to pass a pointer to the object back to managed (I believe managed creates it’s own copy and creates an address for that object) and, using that, we can then try to convert the passed object from a native string into a managed string (via Marshalling).

What about this unmanaged code I keep hearing about? Oh, well… You can find that here. Don’t forget to add the headers referenced in stdafx.h, or your compiler will cry bloody murder.

So, how this works is: The script is called via PowerShell. After some magic to verify that we weren’t given junk data, then PowerShell JIT’s the C# code and performs the runtime operations. The compiler loads the unmanaged DLL into memory. The C# code then calls into the unmanaged DLL via the exposed method (declspec/dllexport/cdecl). The unmanaged code performs it’s work and returns the string back to the caller – unaware that the caller is managed code. The managed code creates an IntPtr to reference the return and then Marshal is called to convert the native string into a managed string. This is then returned back to the user. In the case of multiple instances of a process, the managed string is added to an array and that array is returned.

While it may not seem like much and seem like overglorified complication just to check for threads on wait chaings, it was code that was written to be used in production where we had no desktop environments and we had to determine if this was the case.

I hope that someone gets some kind of use out of it. ๐Ÿ™‚

Until next time.