Making Shared Libraries with Go – Versioned Symbols

Having previously made u2f-luks tools and scripts to allow me to use my surplus U2F (FIDO1) keys as a second factor for disk encryption, it was with some excitement that I read about the newly merged cryptsetup loadable plugin feature. The script used a custom keyscript for cryptsetup, but keyscripts will not be supported by systemd. This meant that the required volumes need to be unlocked and mounted in initramfs with the original cryptsetup tooling, which has its own pitfalls (removable devices etc)

Lets convert my keyscript into a cryptsetup plugin!

How do I convert my u2-luks tool, written in Go, into a shared library for a C program? cgo (godoc) is the Go and C interface layer, and can create shared libraries as nicely documented by other bloggers. The cryptsetup API (libcryptsetup.h) is fairly simple, only one function is required “cryptsetup_token_open”, so this sounded like a perfect evening project, but I ended up spending a few evenings and implementing most of them.

The required function signature from libcryptsetup.h:

typedef int (*crypt_token_open_func) (
	struct crypt_device *cd,
	int token,
	char **buffer,
	size_t *buffer_len,
	void *usrptr);

What does this look like in Go?

package main

// #cgo pkg-config: libcryptsetup
// #cgo LDFLAGS: -Wl,--version-script=cryptsetup_token.map
// #include <errno.h>
// #include <stdlib.h>
// #include <libcryptsetup.h>
import "C"

// ... snip ... 

//export cryptsetup_token_open
func cryptsetup_token_open(cd *C.struct_crypt_device, token C.int, 
    password **C.char, password_len *C.size_t, usrptr *C.char) C.int {

    if cerr := C.crypt_token_json_get(cd, token, &cjson); cerr < 0 {
        C.crypt_log(cd, C.CRYPT_LOG_ERROR,
            C.CString(fmt.Sprintf("token_json_get failed: errno %v\n", -cerr)))
        return -C.EINVAL
    }
    // ... snip ... 
}

The main takeaways I’d like to point out from this:

  • cgo shared libraries are built from the main package
  • C include and #cgo pragmas must appear directly before the import “C”
  • cgo exported functions are lowercase named
  • cgo exported functions have an export comment
  • C structs appear in the C package (with a struct_ prefix), that match types from the included headers.
  • There isn’t a void type directly, its equivalent in Go is unsafe.Pointer, but I didn’t want to use unsafe package, so I left that with a char pointer type, though I don’t plan to use it.
  • C likes negative Enum values as errors (-C.EINVAL), which are easy to use but look a bit weird in Go.
  • I can pass the referenced struct back into other calls in the API to get other needed data than provided in the function signature.
  • Go Strings need converted to C Strings – for both the format and to protect them from garbage collection by the Go Runtime, only pointers created by the C package can be passed into cgo calls.

The most difficult nut to crack was figuring out how to version the exported symbols, I tried not bothering to set any versions, but the module failed loading with symbol not found messages:

$ cryptsetup open --type luks  --debug ./testcontainer testcontainer
[snip]
Trying to load /lib/x86_64-linux-gnu/cryptsetup/libcryptsetup-token-u2f.so
/lib/x86_64-linux-gnu/cryptsetup/libcryptsetup-token-u2f.so: undefined symbol: cryptsetup_token_open, version CRYPTSETUP_TOKEN_1.0
[snip]

I thought I could export the function with a new name:

//export cryptsetup_token_open@CRYPTSETUP_TOKEN_1.0

But the compiler complained:

./main.go:28:1: export comment has wrong name "cryptsetup_token_open@CRYPTSETUP_TOKEN_1.0", want "cryptsetup_token_open"

So, time to read up on how linking works with versioned symbols. Eventually I ended up at GNU ld Manual – Version Scripts that explains how to configure the linker to add the versions, so lets try it out, I created the following file.

CRYPTSETUP_TOKEN_1.0 {
   global:
    cryptsetup_token_open;
    cryptsetup_token_open_pin;
    cryptsetup_token_validate;
    cryptsetup_token_dump;
    cryptsetup_token_version;
};

Adding the #cgo LDFLAGS pragma to say: tell the compiler when linking (LDFLAGS), to pass the linker a flag (-Wl) setting (--version-script) to this file cryptsetup_token.map

// #cgo LDFLAGS: -Wl,--version-script=cryptsetup_token.map 

This however results in the following compiler error

go build github.com/darkskiez/u2f-luks/lukstoken/plugin: invalid flag in #cgo LDFLAGS: -Wl,--version-script=cryptsetup_token.map

Which is fixed by setting CGO_LDFLAGS_ALLOW environment to an appropriate regex, so now the Makefile looks like:

libcrypt-token-u2f.so: main.go cryptsetup_token.map
    CGO_LDFLAGS_ALLOW='-Wl,--version-script=.*' go build -x \
    -buildmode c-shared -o libcrypt-token-u2f.so

Success!

The module loads and works, supports a presence only mode – for unlocking with just a touch, or with a passphrase/pin and presence. I had hoped to make cryptsetup automatically prompt the user when a password was required in addition to the token, but this does not yet work – cryptsetup issue #670 – there are unresolved UX implications that are amplified in severity when using tokens that may self-erase after incorrect guesses (eg. FIDO2 tokens lock out after 8 attempts.)

Unresolved issues: I’d like to use the crypt_safe_memzero or a comparable go function to make sure the key data is securely erased from the go runtime memory, but you cant (safely) pass go memory into C calls (cgo – Passing Pointers), so I’m not sure what would be best here.

My next steps will be investigating how systemd integrates fido2 tokens with cryptsetup, which wasn’t released at the time I wrote this module, and seeing if I can improve the UX with a similar system.

The rest of the code is available on github.com/darkskiez/u2f-luks/tree/master/lukstoken

Use Google Authenticator / OATH for two-factor SSH access with SSH-Keys

I wanted to use two factor auth combined with ssh keys to restrict access to some of the production machines at work, however this wasn’t entirely straight forward as google authenticator pam module would be entirely bypassed with ssh-keys, and only supports one key per account, (so shared accounts like root would be a problem)

I’ve discovered a lovely little simple tool that lets you work around it ssh-opt

+ You can use a different OATH token for each ssh key!
+ You can choose to not require tokens for some keys, eg. for automated systems
+ You can use it along with google authenticator pam for single password + single token access
+ You can install it on machines you don’t have root access to

– It doesn’t support scratch emergency codes or replay protection [yet, it wouldnt be that hard to add]
– It leaks your token key to other users via ps [easily fixable]
– It breaks scp! not sure why yet, it just hangs for me.

Its dead simple to use too, just prefix the key in .ssh/authorized_keys:

command="/usr/bin/ssh-otp OATHTOKEN" ssh-dss AAAAB3...

Fix Linux Flash Player “Error #2046” on iceweasel / debian

This error had plagued me for some time, but the chrome plugin worked… so I didnt mind until I updated to a full 64bit OS, and the chrome plugin does not exist for 64bit

Turns out that the plugin was trying to read the firefox preferences but fails on two accounts, it apparently doesnt handle spaces or relative paths..

Change ~/.mozilla/firefox/profiles.ini something like this:

[General]
StartWithLastProfile=1

[Profile0]
Name=default
IsRelative=0
Path=/home/user/.firefox/default/187x6ax2.slt

to

[General]
StartWithLastProfile=1

[Profile0]
Name=default
IsRelative=1
Path=187x6ax2.slt

and then

ln -s ~/.firefox/default/187x6ax2.slt ~/.mozilla/firefox/

How to add an additional Google Authenticator Device

You will need a rooted device to do this!

I’ve obtained more than one android device, and I was looking at installing the google authenticator app on it as well, however, google wanted me to delete my existing key and create a new one if I was going to configure other devices. This means disabling 2 factor authentication, and I’m not sure if that would mean recreating all of my application specific passwords…but I didnt fancy that..

$ adb shell
# sqlite3 /data/data/com.google.android.apps.authenticator/databases/databases
sqlite> select * from accounts;
1|[email protected]|your2factorkey|0|0
sqlite> .quit
#exit

Now open google authenticator on your new device and choose manually add account, put in your email and key as read above. bish bash bosh, done. Validate this is working by running authenticator on both devices, they should have the same id.

If you dont have a rooted device, you will probably just need to disable and re-setup two-factor authentication to discover your new key.

As a side note, I enjoyed discovering the existence of the following packages:


http://code.google.com/p/mod-authn-otp/
– for adding google two factor auth to your webserver, not sure that this supports scratch codes.


http://code.google.com/p/google-authenticator/
– for adding google two factor auth to your linux machine/services, available on debian as libpam-google-authenticator. It has terminal based ascii-art QR-codes, cool! You can probably just update your ~/.google_authenticator with your key you extracted and also manually enter your scratch codes into this file.

Dear gizoo.co.uk

RE: Anysharp emails.

You’ve sent me emails promoting the LIMITED AVAILABILITY SPECIAL OFFER anysharp on:

12th Aug 2010
21st Aug 2010
12th Oct 2010
16th Nov 2010
4th Jan 2011
25th Jan 2011
29th Jan 2011

And its always, and currently still is, cheaper direct from the manufacturers site than from you.

Please find a better offer.

Also the submit comment on your site was broken, “error ‘8004020e’ /Scripts/email/sendMail.asp, line 119” so i posted this on my blog 🙂

How to reduce the threshold for “Low on Space” Android Warnings

Low on Space – Phone storage space is getting low.

Its a cursed message on my Android HTC Hero, but there is 16MB free on /data partition! I want my email to sync a bit more and I want to receive text messages and I dont want to delete any apps.

You need to have rooted your android device and have the android sdk installed and debugging enabled on your phone. I might package this recipe up into an apk for easy installation.

The default limit is 10% of free space, i’ve reduced mine to 5%, I don’t know if there are any terrible side effects. As you’ve already rooted your phone you’ve already probably voided your warranty 🙂

To reduce from 10% to 5% warning from your “adb shell”:

sqlite3 /data/data/com.android.providers.settings/databases/settings.db
insert into secure (name, value) VALUES('sys_storage_threshold_percentage','5');
insert into gservices (name, value) VALUES('sys_storage_threshold_percentage','5');
.quit

Then reboot your phone.

Some firmwares seem to look for the setting in gservices but the latest android source looks like it looks for it in the secure settings, so i’ve included both for good measure.