Samsung Series 7 Keyboard Backlight Controls Investigations

Background

I did a clean install of Windows 10 on my Samsung Series 7 (740U3E) laptop when I found out about Microsoft’s clean install tool which restores the OS to a pristine state, free from preinstalled vendor bloat-/software, but ended up not using the tool but completely reinstalling from an ISO instead (btw, if you visit the cleaning tool’s webpage while using Linux it will redirect to a page where you can download clean Windows 10 Home/Pro install ISO’s instead of the tool…).

Everything seemed to work fine after the reinstall with just drivers from Windows Update. Everything but some of the hotkeys, and the keyboard backlight that seemed to have a life of its own.

In order not to have to install Samsung Update and Samsung Settings and all services and things that that brings I thought that there must exist some kind of standard method to control the LEDs and enable the hotkeys and tools to do it with. And that was the start of way to many hours of analyzing and testing and getting to learn a little (more) about (U)EFI,  ACPI, disassembly, and debugging, memory layout and management… etc..

I couldn’t find any general tools or even information about how the LED’s were controlled. Besides the linux kernel’s samsung-laptop-module source code which hinted at something called SABI (Samsung Advanced BIOS Interface(?)) which could be used for controlling stuff like the ones one can control with the Samsung Settings application: USB-power in sleep mode, battery life saving mode, silent mode, WiFi and Bluetooth state and keyboard backlight level and eventual timeout. It seemed like a good place to start. I never could figure out how to make SABI calls when EFI-booting though, or if it is even possible, so it was a pretty time consuming detour, I’ve put my notes about it at the end.

So I had to install Samsung Settings anyways in order to get it to work (which was no trivial task in itself as Samsung does not seem to provide any software on their product support pages, but finally found a link to Samsung Update on Samsung’s american site in an old news post, through which other Samsung software and drivers could be installed.).

Attempt at debugging

This didn’t satisfy my curiosity though, so a couple of months later I thought that this question would easily be answered by debugging the controlling Samsung software.

Which led to many frustrating hours with the Windows Debugger (WinDbg) and IDA PRO disassembler. I expected to be able to kindof (relatively) quickly find a simple write to some memory address(es) that triggered the changes, and then be able to repeat them. But I didn’t.

Don’t know if it was because of intentional obfuscation reasons or something else, but for some reason the 32-bit EasySettingsCmdServer.exe through a stack of calls though SUS.dll (SetKeyboardBacklitLevel())and WSABI.dll (CallBIOSInterface())

makes a call to 64-bit code through ntdll!Wow64Transition/ntdll!Wow64SystemServiceCall, where 32-bit debuggers/disassemblers cannot follow. My very limited experience and understanding of these topics put a stop to  my investigations here after attempting to debug with WinDbg Preview which seemed to handle the switch (but I’m not sure) but led too deep into debuggers and assembly and windows internals for me to follow, one example trace of an SUS.dll!SetKeyboardBacklitLevel invocation:

Tracing SUS!SetKeyboardBacklitLevel to return address 739bc345
    7     0 [  0] SUS!SetKeyboardBacklitLevel
    4     0 [  1]   SUS!SetMaxPerformanceStatus
    3     0 [  2]     SUS!SetMaxPerformanceStatus
   17     0 [  3]       SUS!SetMaxPerformanceStatus
   14    17 [  2]     SUS!SetMaxPerformanceStatus
   10     0 [  3]       SUS!SetMaxPerformanceStatus
   16     0 [  4]         ntdll!RtlEnterCriticalSection
   17    16 [  3]       SUS!SetMaxPerformanceStatus
    5     0 [  4]         KERNEL32!TlsGetValueStub
   11     0 [  4]         KERNELBASE!TlsGetValue
   25    32 [  3]       SUS!SetMaxPerformanceStatus
   23     0 [  4]         ntdll!RtlLeaveCriticalSection
   32    55 [  3]       SUS!SetMaxPerformanceStatus
   19   104 [  2]     SUS!SetMaxPerformanceStatus
   11     0 [  3]       SUS!SetMaxPerformanceStatus
   20   115 [  2]     SUS!SetMaxPerformanceStatus
   12   135 [  1]   SUS!SetMaxPerformanceStatus
    3     0 [  2]     SUS!SetMaxPerformanceStatus
   18     0 [  3]       SUS!SetMaxPerformanceStatus
    9    18 [  2]     SUS!SetMaxPerformanceStatus
   11     0 [  3]       SUS!SetMaxPerformanceStatus
   10    29 [  2]     SUS!SetMaxPerformanceStatus
   15   174 [  1]   SUS!SetMaxPerformanceStatus
   10   189 [  0] SUS!SetKeyboardBacklitLevel
    6     0 [  1]   SUS!SetMaxPerformanceStatus
    3     0 [  2]     SUS!SetMaxPerformanceStatus
    3     0 [  3]       SUS!SetMaxPerformanceStatus
   17     0 [  4]         SUS!SetMaxPerformanceStatus
   14    17 [  3]       SUS!SetMaxPerformanceStatus
   10     0 [  4]         SUS!SetMaxPerformanceStatus
   16     0 [  5]           ntdll!RtlEnterCriticalSection
   17    16 [  4]         SUS!SetMaxPerformanceStatus
    5     0 [  5]           KERNEL32!TlsGetValueStub
   11     0 [  5]           KERNELBASE!TlsGetValue
   25    32 [  4]         SUS!SetMaxPerformanceStatus
   23     0 [  5]           ntdll!RtlLeaveCriticalSection
   32    55 [  4]         SUS!SetMaxPerformanceStatus
   19   104 [  3]       SUS!SetMaxPerformanceStatus
   11     0 [  4]         SUS!SetMaxPerformanceStatus
   20   115 [  3]       SUS!SetMaxPerformanceStatus
   11   135 [  2]     SUS!SetMaxPerformanceStatus
    3     0 [  3]       SUS!SetMaxPerformanceStatus
   18     0 [  4]         SUS!SetMaxPerformanceStatus
    9    18 [  3]       SUS!SetMaxPerformanceStatus
   11     0 [  4]         SUS!SetMaxPerformanceStatus
   10    29 [  3]       SUS!SetMaxPerformanceStatus
   14   174 [  2]     SUS!SetMaxPerformanceStatus
   15   188 [  1]   SUS!SetMaxPerformanceStatus
    5     0 [  2]     KERNEL32!ActivateActCtxStub
    4     0 [  2]     KERNELBASE!ActivateActCtx
   11     0 [  3]       KERNELBASE!IsActivateActCtxWorkerPresent
    8    11 [  2]     KERNELBASE!ActivateActCtx
    9     0 [  2]     KERNEL32!ActivateActCtxWorker
   17     0 [  3]       ntdll!RtlActivateActivationContext
   29     0 [  4]         ntdll!RtlActivateActivationContextEx
   52     0 [  5]           ntdll!RtlpAllocateActivationContextStackFrame
    3     0 [  6]             ntdll!__security_check_cookie
   55     3 [  5]           ntdll!RtlpAllocateActivationContextStackFrame
   79    58 [  4]         ntdll!RtlActivateActivationContextEx
   22   137 [  3]       ntdll!RtlActivateActivationContext
   15   159 [  2]     KERNEL32!ActivateActCtxWorker
   21   386 [  1]   SUS!SetMaxPerformanceStatus
   15   596 [  0] SUS!SetKeyboardBacklitLevel
   17     0 [  1]   SUS
    5     0 [  2]     advapi32!RegOpenKeyExWStub
   11     0 [  2]     KERNELBASE!RegOpenKeyExW
   54     0 [  3]       KERNELBASE!RegOpenKeyExInternalW
   27     0 [  4]         KERNELBASE!MapPredefinedHandleInternal
   32     0 [  5]           ntdll!RtlGetCurrentTransaction
   30    32 [  4]         KERNELBASE!MapPredefinedHandleInternal
   34     0 [  5]           ntdll!RtlSetCurrentTransaction
   32    66 [  4]         KERNELBASE!MapPredefinedHandleInternal
   16     0 [  5]           ntdll!RtlAcquireSRWLockExclusive
   49    82 [  4]         KERNELBASE!MapPredefinedHandleInternal
   14     0 [  5]           ntdll!RtlReleaseSRWLockExclusive
   52    96 [  4]         KERNELBASE!MapPredefinedHandleInternal
   34     0 [  5]           ntdll!RtlSetCurrentTransaction
   67   130 [  4]         KERNELBASE!MapPredefinedHandleInternal
    3     0 [  5]           KERNELBASE!__security_check_cookie
   70   133 [  4]         KERNELBASE!MapPredefinedHandleInternal
   62   203 [  3]       KERNELBASE!RegOpenKeyExInternalW
  201     0 [  4]         ntdll!RtlInitUnicodeStringEx
   77   404 [  3]       KERNELBASE!RegOpenKeyExInternalW
   73     0 [  4]         KERNELBASE!LocalBaseRegOpenKey
    3     0 [  5]           ntdll!NtOpenKeyEx
    1     0 [  6]             ntdll!Wow64SystemServiceCall
    1     0 [  6]             0x65117000
KBD_backlit_Auto On

And after reading articles like these I realized that it was probably nothing that I quickly could learn to a sufficient degree without significant additional time investment:

https://reverseengineering.stackexchange.com/questions/16200/how-to-investigate-windows-32-64bit-wow64-transition

It also seems to make WMI-calls and do something related to ACPI0008 (driver name: Light Sensor), but I’m too unskilled with the debugger to tell which are related and not.

ACPI

Instead, while trying to find out how to make a SABI call I found out about this tool FWTS (“Firmware Test Suite (FWTS) is a test suite that performs sanity checks on firmware. It is intended to identify BIOS, UEFI, ACPI and many other errors and if appropriate it will try to explain the errors and give advice to help workaround or fix firmware bugs. It is primarily intended to be a Linux-centric firmware troubleshooting tool.”). Running it provided an wealth of interesting hardware information.

Somehow this led me to the ACPI tables, the decompilation of them from “ACPI Machine Language” to “ACPI Source Language”) (fwts dumpacpi, iasl -d), and while reading through the DSDT (Differentiated System Description Table) table’s decompiled source code I happened upon some interestingly named variables (See e.g. Arch Linux DSDT wiki article for more information):

OperationRegion (SNVS, SystemMemory, 0xC9F67D98, 0x0100)
Field (SNVS, AnyAcc, Lock, Preserve)
{
    OSTP,   16, 
    SMIS,   8,     // Offset 0x02
    DB00,   8, 
    DW00,   16, 
    /.../ 
    KBTO,   8,     // Offset 0x77
    Offset (0x7C), 
    KBST,   8,     // Offset 0x7C
    KBLL,   8,     // Offset 0x7D
    /.../
}

Which gave me a memory address (0xC9F67D98) and offsets to KBTO, KBST and KBLL, which I guessed was KeyBoard TimeOut, STatus and LightLevel or something like that.

I wrote getter and setter shell scripts that reads/writes from/to these addresses though /dev/mem:

#!/bin/bash
for i in KBLL,$((0xC9F67D98 + 0x7d)) \
        KBST,$((0xC9F67D98 + 0x7c)) \
        KBTO,$((0xC9F67D98 + 0x77))
do
    IFS=',' read k v <<< "${i}"
    echo -n "${k}: "
    dd if=/dev/mem skip=${v} bs=1 count=1 2>/dev/null | hexdump -e '"%x"'
    echo
done
#!/bin/bash
if [[ $# -ne 2 ]]; then
    echo "Usage setkbd {LL,ST,TO} hexval."
    exit
fi

case "${1^^}" in
 KBLL | LL)
    addr=$((0xC9F67D98 + 0x7d))
    ;;
 KBST | ST)
    addr=$((0xC9F67D98 + 0x7c))
    ;;
 KBTO | TO)
    addr=$((0xC9F67D98 + 0x77))
    ;;
 *) 
    echo "Unknown argument $1"
    echo "Usage setkbd {LL,ST,TO} hexval."
    exit
    ;;
esac

printf "\x${2}" | dd of=/dev/mem seek=$addr bs=1

Just writing to the addresses had no effect though. I had however noticed that the backlight turned on when the computer awoke from sleep (sometimes, or always since I taped over my ambient light sensor). Only from sleep though, not at power on, so the lights would remain off until the computer first was put to sleep and awoken and then they would turn on. So i hypothesized that maybe that could be used as a trigger. Which it could.

So I could set KBLL to a value 0 – 4 (printf "\x04" | dd of=/dev/mem seek=$((0xC9F67D98 + 0x7D)) bs=1)and then put the computer to sleep (fwts s3 -, shutting the lid)and when it woke up it would use that brightness level.  At least I now got a (somewhat cumbersome) way to set the light level!

I booted Windows and had a peek at the addresses (using RW Everything) while I was changing timeout settings and light level using Samsung Settings and found that only KBLL seemed to change from 0 to 4 depending on level chosen. KBTO seemed to have nothing to do with timeout, KBLL just changed from a value > 0 to 0 at timeout and back to whatever it was again upon next user input. I’m guessing that the timeout stuff and hotkeys (that reports ordinary scancodes like ordinary keys) are handled by software under windows.

In the DSDT table there fortunately existed a method (it is a pretty strange and almost superfluous one and without it I would not have come to this solution) that is executed when the computer wakes from sleep that seemingly reads from the ambient light sensor and turn on the keyboard backlight if the reading is below a certain value and turn it off if above another value (unverified) if KBTO <> 0 (verified). This is the only place in the table that the KBxx values seemed to be referenced so searches for those symbols led there:

Method (_Q70, 0, NotSerialized)  // _Qxx: EC Query
{
    If (((ALSE == 0x02) && IGDS))
    {
        Local0 = ^ALSD._ALI ()
        ^^^GFX0.AINT (Zero, Local0)
        Notify (ALSD, 0x80) // Status Change
    }

    Local0 = LUXH /* \_SB_.PCI0.LPCB.H_EC.LUXH */
    Local0 = ((Local0 << 0x08) | LUXL) /* \_SB_.PCI0.LPCB.H_EC.LUXL */
    Local0 = ^ALSD.CLUX (Local0)
    If ((KBTO == Zero))
    {
        If (((Local0 <= 0x14) && (KBST != One))) 
        { 
            SECS (0x9A) 
            KBST = One 
        } 
        ElseIf (((Local0 >= 0x32) && (KBST != Zero)))
        {
            SECS (0x9B)
            KBST = Zero
        }
    }
}

(one annoying this is that most ACPI symbols are four letters long so it is not immediately obvious what they stand for… so by guessing CLUX(LUXH/LUXL) = some kind of normalized measurement from the light sensor (high/low byte)).

When status/state(?) is set to 1 the SECS() function is called with argument 0x9A, when to 0 with 0x9B. So maybe we could simulate those SECS()-calls to trigger a change. Jumping to the definition…

Method (SECS, 1, Serialized)
{
    Acquire (MSEC, 0xFFFF)
    Store (Arg0, SMIS) /* \SMIS */
    Store (SWCD, TRPS) /* \TRPS */
    Release (MSEC)
}

(SECS, guessing something about the EC (Embedded Controller), SMIS reminds of SwSmi, TRPS maybe TRigger something (Power State? 0xB2 (but now we’re getting ahead of ourselves) is sometimes refered to as the APM port?)? SWCD also reminicent of SwSmi…).

We can here see that the argument to SECS, 0x9A or 0x9B, is stored in SMIS and SWCD is written to TRPS.

Moving on to MSEC mutex revealed TRPS/SECR and led on onto SSMI:

Mutex (MSEC, 0x00)
OperationRegion (SECR, SystemIO, SSMI, 0x02)
Field (SECR, ByteAcc, NoLock, Preserve)
{
    TRPS,   8
}

SWCD and SSMI were constants:

Name (SSMI, 0xB2)
Name (SWCD, 0xA8)

And the SMIS variable was to be found in the same SNVS region as the KBxx ones at 0xC9F67D98, offset 0x02:

OperationRegion (SNVS, SystemMemory, 0xC9F67D98, 0x0100)
Field (SNVS, AnyAcc, Lock, Preserve)
{
    OSTP,   16, 
    SMIS,   8,     // Offset 0x02
    DB00,   8, 
    DW00,   16, 
    /.../ 
    KBTO,   8,     // Offset 0x77
    Offset (0x7C), 
    KBST,   8,     // Offset 0x7C
    KBLL,   8,     // Offset 0x7D
    /.../
}

Now I had all the information needed to make the calls by emulating this by writing  to /dev/mem and /dev/port:

# Write "command" 0x9A to enable or 0x9B to disable to SMIS (0xC9F67D98 + 0x02)
printf "\x9A" | dd of=/dev/mem seek=$((0xC9F67D98 + 0x02)) bs=1

# Write desired brightness level 0-4 (8 on some models) to KBLL (0xC9F67D98 + 0x7D)
printf "\x04" | dd of=/dev/mem seek=$((0xC9F67D98 + 0x7D)) bs=1

# Write "SWCD" (0xA8), to SMI Command IO Port "SSMI" (0xB2) to trigger change
printf "\xA8" | dd of=/dev/port seek=$((0xB2)) bs=1

And (to my surprise) it works. Writing 0x9A to SMIS will indeed enable the backlight and 0x9B will disable it. Writing 0 – 4 to KBLL will set light level when enabled. Writing A8 to port B2 is what will trigger the change.

This doesn’t align too well with the values of the commands and arguments as the BIOS SABI calls as per the kernel code. But works well enough. Now I had a way to control keyboard backlight without having to put the computer to sleep to trigger the change. Mission accomplished!

Looking through the (U)EFI driver names (given by UEFItool) one gets the impression that there are two ways of changing settings, the SABI way and the SMM (System Management Mode, google “SSM B2”) way (the existence of an EFI variable named LegacySabiBuffPtr also supports this). (However, SABI also make use of port 0xB2):

SABI
/*
 * This driver is needed because a number of Samsung laptops do not hook
 * their control settings through ACPI.  So we have to poke around in the
 * BIOS to do things like brightness values, and "special" key controls.
 */

/*
 * We have 0 - 8 as valid brightness levels.  The specs say that level 0 should
 * be reserved by the BIOS (which really doesn't make much sense), we tell
 * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
 */
#define MAX_BRIGHT	0x07

The kernel module source disclosed that SABI calls were made by writing commands and arguments to a specific memory addresses and output to a port.

The first thing that the module does is to check if we’re EFI-booting. If so initialization will fail and the module not load. The reason for this is that some Samsung models will brick themselves it EFI non-volatile variable storage memory becomes more than 50% full so the kernel devs did not think it safe to peek/poke around in memory then:

static int __init samsung_init(void)
{
    struct samsung_laptop *samsung;
    int ret;

    if (efi_enabled(EFI_BOOT))
        return -ENODEV;
/.../
}

The port, the values to output to the port and the pointer to the SABI call memory address is found by searching for the string “SwSmi@” in the memory segment where BIOS is mapped, 64kb under 1Mb, 0xf0000 to 0xfffff. The bytes following that string then gives us:

.header_offsets = {
    .port = 0x00,
    .iface_func = 0x02,
    .en_mem = 0x03,
    .re_mem = 0x04,
    .data_offset = 0x05,
    .data_segment = 0x07,
}

SwSmi@[port:2][iface:1][en_mem:1][re_mem:1][data_offset:2][data_segment:2]

In a hexdump from that memory segment from my system I found:

“SwSmi@” b2 00 80 81 82 00 f0 90 d2

data_offset and data_segment makes up a pointer to SABI call interface memory area and is given given by the expression (data_segment << 4 | data_offset).

That makes port=0x00B2, iface=0x80, en_mem=0x81, re_mem=0x82 and pointer = (0xf000 << 4 | 0xd290) = 0x000fd290.

The source code for sabi_command() and some defines tells us how a SABI call is made:

#define SABI_IFACE_MAIN			0x00
#define SABI_IFACE_SUB			0x02
#define SABI_IFACE_COMPLETE		0x04
#define SABI_IFACE_DATA			0x05

/.../

/* enable memory to be able to write to it */
outb(readb(samsung->sabi + config->header_offsets.en_mem), port);

/* write out the command */
writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN);
writew(command, samsung->sabi_iface + SABI_IFACE_SUB);
writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE);
if (in) {
    writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA);
    writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4);
    writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8);
    writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10);
}
outb(readb(samsung->sabi + config->header_offsets.iface_func), port);

/* write protect memory to make it safe */
outb(readb(samsung->sabi + config->header_offsets.re_mem), port);

/* see if the command actually succeeded */
complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE);
iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA);

This means the call is made by writing following to in my case 0x000fd290:

[main_funtion:2][command:2][complete:1][d0:4][d1:4][d2:2][d3:1]

Main function is always 0x5843, the command for changing the backlight 0x0078, and first parameter d0 = (0x82 | level << 8). The backlight first has to be enabled by issuing command 0xaabb though, all given by:

/* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */
u16 kbd_backlight;
/.../
kbd_backlight = 0x78

So we should be able to control the backlight by something like:

# Enable the bl
printf "\x81" | dd of=/dev/port seek=$((0xB2)) bs=1 2>/dev/null
printf "\x43\x58\x78\x00\x00\xbb\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | dd of=/dev/mem seek=$((0xfd290)) bs=1 2>/dev/null
printf "\x80" | dd of=/dev/port seek=$((0xB2)) bs=1 2>/dev/null
printf "\x82" | dd of=/dev/port seek=$((0xB2)) bs=1 2>/dev/null
sleep 1
hexdump -C -s $((0xfd290)) -n 20 /dev/mem
# complete, written 0x00, should read back 0xaa, on error d0 low byte should read 0xff
# d0, written 0xaabb, should now be read back 0xccdd

sleep 10

# Set light level
printf "\x81" | dd of=/dev/port seek=$((0xB2)) bs=1 2>/dev/null
printf "\x43\x58\x78\x00\x00\x82\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" | dd of=/dev/mem seek=$((0xfd290)) bs=1 2>/dev/null
printf "\x80" | dd of=/dev/port seek=$((0xB2)) bs=1 2>/dev/null
printf "\x82" | dd of=/dev/port seek=$((0xB2)) bs=1 2>/dev/null
sleep 1
hexdump -C -s $((0xfd290)) -n 20 /dev/mem
# complete, written 0x00, should read back 0xaa, on error d0 low byte should read 0xff

(If you’re noticing that the order of bytes written seems a bit of fit is because of little-endianess. Where we have writeb() we can just printf the byte. Where we have writew() we have to write the word least-signifiant-byte-first, so main-function 0x5843 becomes printf “\x43\x58”. Same with writel(), 0x0000aabb becomes printf “\xbb\xaa\x00\x00”.)

This method does not work however. Nothing happens.

The “SwSmi@” string seems to me to be nowhere to be found in memory (where I’ve peeked) unless having the CSM (“The Compatibility Support Module (CSM) is a component of the UEFI firmware that provides legacy BIOS compatibility by emulating a BIOS environment, allowing legacy operating systems and some option ROMs that do not support UEFI to still be used.”…) enabled. And I’d prefer to not enable that. And even if I did the kernel module won’t initialize if EFI booting anyways, a check for this is made at startup and the module is not initialized (could easily be changed though).

(The reason for not wanting to enable the CSM is that it had certain unpredictable side effects on my stationary computers. Firstly, the option that enable/disable it is there named “Windows 8 Mode”. Not entirely obvious that enabling that option means disabling CSM. Having CSM enabled will in its turn limit the EFI framebuffer resolution to something like 1024×768 though, another not-too-obvious connection. It doesn’t matter too much on my media-server which has an Intel video card that has framebuffer driver support that can take over after the EFI framebuffer a bit later in the boot sequence and up the resolution to my TV’s 1920×1080. It was an annoyance on my desktop computer that has an NVidia 1060 card… The proprietary NVidia drivers does not have framebuffer support that can take over after the EFI one which meant that I was left with ugly low-res consoles instead of nice 2560×1440 ones. Or so I thought. Enabling “Windows 8 Mode”, disabling CSM, enabled much higher EFI-framebuffer resolutions. So now even grub is in monitor-native res. Took many years (and three NVidia cards) before I figured that one out.)

There exists at least two EFI variables which seems to be related. KBDBacklitLvl and LegacySabiBuffPtr. Writing to the Kbd-variable does nothing however and the the LegacySabiBuffPtr holds the same memory address that can be found by searching for “SwSmi@” with the CSM on, 0x000fd270.

Tumblr default theme, auto load external images

Tumblr’s default theme doesn’t support external images any longer. It neither auto-loads them nor give you a “Load external image”-link for click-to-load like it used to and like it still does on the dashboard. Only the plain text placeholder “External image” is displayed where the image used to load.

One can restore the old auto-load functionality by editing the theme and adding the following little code snippet somewhere, e.g. right before the </body> tag.

<script type="text/javascript">
    (function($) {
        $(window).scroll(function() {
            $('div.external-image-wrapper').each(function() {
                $(this).replaceWith("<img src='" + $(this).attr("data-src") + "' />")
            });
        });
    })(window.jQuery);
</script>

 

Batman: Arkham Knight – City Heat TT AR

My best City Heat TT time, placing me on the 55th position on the world ranking list. Not to bad for someone who’d only borrowed a PS4 to play through the game and otherwise almost newer plays on consoles or with a gamepad. The batmobile handling and timing rules (drifting slows timer and recharges turbo boost) takes a bit of getting used to though so it is seldom the best/faster looking races that yields the best times… Placed 64th and 120th on the game’s other two TT tracks.

SSH: iptables recent, pknock; google-authenticator; kerberos

Basic SSH

The only port that I have open in my firewall is 22, SSH. Through one can access all other services by means of ssh tunnels, SFTP, scp etc..  I used to leave it at that, just disallowing root logins (from IP-addresses outside my LAN) as most of the time the attacker tried to use username root and I thought that at least that would void those brute force attempts.

But the SSH port gets hammered relentlessly around the clock and the log files grow to be humongus so I thought I’d try to do something at least try to limit the attacks.

iptables recent

So I started by using the iptables recent module to blacklist IP-addresses that tries to connect more than 4 times within 10 minutes to port 22 for 1 hour (i think). But there were still quite a lot of hammering going on as many different source IP’s were used, and, most annoyingly you could (and I did on multiple occasions) lock yourself out by starting more that 4 ssh sessions within 10mins (FileZilla for example quickly does this for you  if you let it use multiple parallel connections (the default) when downloading files). But for a long time this worked well enough I thought.

The iptables commands to set this up is somewhat cryptic:

iptables -A INPUT -m recent --update --seconds 600 --hitcount 4 --name blacklist --mask 255.255.255.255 --rsource -j DROP
iptables -A INPUT -m recent --set --name blacklist --mask 255.255.255.255 --rsource -j ACCEPT
Google-Authenticator-LibPAM

Later, after having turned on TOTP two-factor-authentication on all of my online accounts with that option I wanted to enable the same for SSH logins to my machines and found that this was easily realized by utilizing google-authenticator-libpam.

All one had to do was to build and install the PAM-module and add one line to /etc/pam.d/system-remote-login, requiring pam_google_authenticator.so (nullok makes it so that if a user haven’t setup google-authenticator for his account the login is allowed).

auth        required    pam_tally2.so onerr=succeed
auth        required    pam_shells.so
auth        required    pam_nologin.so
auth        required    pam_env.so
auth        required    pam_unix.so try_first_pass
auth        required    pam_google_authenticator.so echo_verification_code debug nullok
auth        optional    pam_permit.so

account     include     system-login
password    include     system-login
session     include     system-login

Then the user runs a google-authenticator program which generates a secret key, provides you with a number of backup keys (as per the norm), and stores information about this in a dotfile in your home directory that the PAM module will look for. The script even displays a QR-code in the terminal window so you easily can scan it into your phone’s TOTP app!

google-authenticator
google-authenticator
iptables pknock

Then I asked myself if port knocking wouldn’t make a lot more sense that rate-limiting. Then the port would be completely hidden and practically no attack attempts would reach it. Port knocking is a technique whereby one make connections to a specific sequence of ports within a given time frame to open a hole in the firewall to the port you want to give access to to the knocking ip. I chose the iptables pknock module to enable this. It is not included in the main kernel tree but available as an external module. Using gentoo one can set XTABLES_ADDONS=”pknock” in make.conf and then emerge xtables. After setting this up i wondered why I had not just done this from the start, so easy and efficient.

A simple iptables rule that says that we have to make tcp connections to ports 1234, 5678 and 5555 in that order and within 10s to grant access to port 22 for 10min (access to setup connections, ssh sessions can last longer than that but that is handled by another conntrack rule that allows any established connections to continue.)

iptables -A INPUT -p tcp -m pknock --knockports 1234,5678,5555 --time 10 --autoclose 10 --name SSH --strict -m tcp --dport 22 -j ACCEPT

A simple knock-script that uses nc (netcat) utility to connect to the defined ports in sequence for 1s 1s apart.

#!/bin/sh

for x in 1234 5678 5555
do
    nc -z -w 1 hades.eleusis.se $x &>/dev/null
    echo $x
    sleep 1
done

A rather annoying thing with this I notice is that if you build a new kernel and forgot to re-emerge (emerge @modules-rebuild) the xtables package, iptables-restore will fail with missing module error on startup and leave the firewall completely open when one boots one’s new kernel. This is not that easily spotted on a headless server machine. I would have thought it would have made more sense to leave it completely closed in such cases. It would be more secure and one would immediately notice that something was wrong and have the chance to timely remedy the situation. Hopefully I won’t forget again (or maybe I should look into the possibility to have iptables DENY instead of ALLOW by default and make the change).

Kerberos

I later added support for kerberos based authentication in my LAN, following this excellent and still surprisingly,  since written 2007, valid guide (imagine if it had been a latest-javascript-lib-guide… the lib would like not even exist in any recognizable shape of form any longer…). I could follow the guide step by step, just enter the commands one by one, and it would work!: [HOWTO] Kerberos for small networks, without LDAP or AD.

Before reading that howto I had not thought kerberos was possible, or feasible, without a directory server. But afterwards I had realized that a setup like one I would have liked to have actually was not only possible, but pretty simply so, and without any questionable hacks.

I wanted to be able to ssh to any machine in the network without providing any passwords if a valid kerberos ticket was held and the kerberos ticket granting server accessible (which should be only from within my LAN). If trying to connect without ticket or access to the server (I’m actually not really sure about this, maybe access to server is not necessary if one only holds a currently valid ticket?) one would be asked for password and TOTP like before.

I solved this by emerging mit-krb5, setting up a most basic /etc/krb5.conf and a /etc/krb5.keytab with the host principal’s key) and emerging the pam_krb5 package and altering system-remote-login as so:

auth        required    pam_tally2.so onerr=succeed
auth        required    pam_shells.so
auth        required    pam_nologin.so
auth        required    pam_env.so
auth        sufficient  pam_krb5.so force_first_pass minimum_uid=1000
auth        required    pam_unix.so try_first_pass
auth        required    pam_google_authenticator.so echo_verification_code debug nullok
auth        optional    pam_krb5.so force_first_pass minimum_uid=1000
auth        optional    pam_permit.so

account     include     system-login
password    include     system-login
session     include     system-login

The first pam_krb5.so row makes it so that if a user holds a valid kerberos ticket that is sufficient for the login to be allowed, force_first_pass makes it not ask for a password but only use one previously provided, if any, in this case that would be a ticket). No unix password nor OTP will be asked for in that case. The second almost identical row which is marked optional makes it so that if we don’t hold a ticket and move on to unix password and TOTP authentication it will try to use the entered unix password to automatically request a new ticket from the granting server, which potentially can save one from another kinit command with accompanying manual password entry, as long as the local unix and kerberos server passwords match. Otherwise one has to update the local password to match the servers (as we are using kerberos without any directory server or such, even if the pam module can keep the passwords synchronized when changing on one host the change of local password will not automatically propagate to other hosts.).

WSL

This works nicely with the Windows Subsystem for Linux. You can install the kerberos packages, copy the /etc/krb5.conf and use kinit to authenticate within your WSL session. Very neat.

Current Status

So now my server support kerberos authentication within the LAN so we can ssh freely between the hosts without providing credentials other than the ticket that is granted at first login on any machine. A very convenient thing is that one can specify which user one would like to authenticate as. The root user has no kerberos principal (only local logins) and is not permitted to log in via SSH anyways. But by executing “kinit mignon” the root user can be granted a ticket which provides SSH access as mignon, kind of neat. So instead of “ssh mignon@hades” and having to give password and OTP one can use “kinit mignon && ssh mignon@hades” and only give the password and keep the phone in the pocket. Access from outside of the LAN requires port knocking before a connection can be made, and then password and OTP like before.