Use notify-send as root

The automount script is a neat devd based automounter for FreeBSD. Just pkg install automount and all your removable media will mount themselves automatically in /media when you plug them in. It’s very clean. You may also check vumount, a short script that I made to list all removable media and remove the mount point when you unmount them.

It’s possible to configure automount to send a notification to your desktop using notify-send from libnotify. What it does exactly is (as root):

env DISPLAY=:0 notify-send automount "Device '${1}' mounted on '${3}' directory."

Except that it doesn’t work… I started dbus-monitor and tried notify-send as root (from ttyv2) but didn’t receive anything and notify-send did not complain. So I tried to start dbus-monitor from root instead, in the hope that it would be a bit more verbose than notify-send. I got this error message:

Failed to open connection to session message bus: Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.

By default DBus sessions are private and don’t accept connections from other users than the one that own the bus, even root. The solution is to configure DBus to allow root on the session bus. Edit /usr/local/etc/dbus-1/session.conf and add this line to the default policy:

<allow user="root"/>

But you still need tell DBus how to connect to the session bus. To do so you have to specify the session bus address in DBUS_SESSION_BUS_ADDRESS. Fortunately automount is a shell script, so you can fetch and export the bus address from its configuration file. Just add this to /usr/local/etc/automount.conf:

# Load DBus session bus address
DBUS_USER=your-user
if [ -d /home/$DBUS_USER/.dbus/session-bus ]
then
  dbus_file=$(ls -t1 /home/$DBUS_USER/.dbus/session-bus | head -n1)
  export DBUS_SESSION_BUS_ADDRESS=$(cat /home/$DBUS_USER/.dbus/session-bus/$dbus_file | \
                                    grep "DBUS_SESSION_BUS_ADDRESS=" | \
                                    sed 's/[A-Z_]*=//')
fi

Data signing failed

I got the following error while trying to send a signed (GPG) e-mail using claws-mail:

Signature failed: Data signing failed, General error

Turns out it was a path problem. The GPG agent uses pinentry to ask for your private key password. The agent was configured with the absolute path to pinentry-gtk-2 on Linux. But this happened on FreeBSD and executables are located in /usr/local, not /usr.

So I changed in ~/.gnupg/gpg-agent.conf:

- pinentry-program /usr/bin/pinentry-gtk-2
+ pinentry-program /usr/local/bin/pinentry-gtk-2

Switch MTA on FreeBSD

As you probably know FreeBSD comes with Sendmail installed as the default MTA. However this may be a bit overkill on a desktop installation where the most you might want is to relay mails to an external address. Luckily it is quite easy to change the default MTA as described in the handbook, see 28.4. Changing the Mail Transfer Agent.

On my Desktop I prefer to install nullmailer. This is a simple MTA replacement for hosts which only relay mails through a smart relay. GNUTLS (SSL) is not enabled by default in the nullmailer package on FreeBSD. So if you want SSL you have to compile the port. This is my case. Let’s install it:

cd /usr/ports/mail/nullmailer
make install clean
(...)
pkg lock nullmailer

The configuration happens in /usr/local/etc/nullmailer. This directory contains multiple files and each one of them focuses on a specific aspect of the configuration.

First we specify the remote SMTP through which our mail shall be relayed, this is the remotes file. This file contains a list of remote servers, the module used to send the message and command-line arguments for that module. Modules are located in /usr/local/libexec/nullmailer. The man page states that you can list available options using --help on each protocol module.

In most cases you want to use the smtp module which takes the following arguments (with SSL enabled):

  • port: SMTP port (25, 465, 587, …)
  • user: SMTP user
  • pass: SMTP password
  • auth-login: LOGIN authentication method (default to PLAIN)
  • ssl: Use SSL/TLS encryption
  • starttls: Use STARTTLS command to initiate encrypted connection
  • insecure: Accept invalid certificates (which I do not recommend)
  • x509certfile: Client certificate file
  • x509cafile: Certificate Authority trust file (default to /etc/ssl/cert.pem on FreeBSD)
  • x509crlfile: Certificate revocation list
  • x509fmtder: Switch from PEM to DER format for the certificates

Here is an example that would relay through relay.example.com:465 using SSL and LOGIN authentication:

relay.example.com smtp --port=465 --ssl --auth-login --user=some-user --pass=some-password

Since this file contains your SMTP password in cleartext, I advise you to:

chown nullmail:nullmail remotes
chmod 600 remotes

Next we edit the name that will be used to construct email addresses on this host. You configure this in the me file. Normally this should be the fully-qualified host name of the computer running nullmailer. This is really useful to distinguish, say root at machine-a from root at machine-b. However some mail providers refuse to relay mails from a different domain name than their own so it might be useful to change this in those cases (I am my own mail provider, so personally I don’t care and do what I want). You also need to configure defaultdomain to your domain name. That is your FQHN minus the hostname. If a mail is sent to an address that is not localhost and does not contain a domain name (no period in the hostname), this domainname will be appended to it.

After that we configure the mail to which all local mails are forwarded. You configure this address in the adminaddr file. And we also configure the file pausetime. This is the interval of time between two queue runs with a default value of 60 seconds. I prefer to set this to a higher value, like 15 minutes.

For more information about the configuration of nullmailer, see this article. Although related to Raspbian on a RPi, it remains mostly the same.

Now we need to replace the MTA on FreeBSD. First we configure the mailwrapper (see man mailwrapper) in /etc/mail/mailer.conf. Replace each line with their nullmailer equivalent, that is:

sendmail  /usr/local/libexec/nullmailer/sendmail
send-mail /usr/local/libexec/nullmailer/sendmail
mailq     /usr/local/libexec/nullmailer/mailq

Time to test. Disable sendmail, enable nullmailer and send a mail. Oh and by the way, tail -f /var/log/maillog in any case:

service sendmail stop
service nullmailer onestart
echo Hello from FreeBSD\! | mailx -s "test" root

If it works, you can now disable sendmail and enable nullmailer in /etc/rc.conf:

sendmail_enable="NONE"
nullmailer_enable="YES"

Where did my PGP keys go?

Today I noticed that one of my PGP private key just disappeared of GPG. The key did not appear when I did gpg --list-secret-keys. After a bit of investigation I discovered that the problem did not affect Linux hosts but only FreeBSD hosts. Weird…

The source of the problem was a migration from GnuPG v2.0 to v2.1. According to this page, GPG does not handle the private keys anymore and delegates all private keys operations to the gpg-agent. Therefore GPG v2.1 migrates the legacy secret keyring, secring.gpg, to the gpg-agent key store, private-keys-v1.d and then forgets about it.

Though, you see, my GPG keyrings were synchronized across all hosts. But the GnuPG package on Debian is still v2.0, while FreeBSD is v2.1. Get the picture?

I synced my keyring on FreeBSD hosts where GPG migrated my private keys to the gpg-agent key store. Then I generated a new key pair on a Debian host, which was added to the legacy keyring. Resynced, but the newer version of GPG didn’t care, they already migrated to the new key store.

Fortunately it was easy to fix, all you have to do is re-import your legacy keyring with one of the newer versions of GPG. The private keys are now also present in the new key store so you can sync to all other hosts.

gpg --import $HOME/.gnupg/secring.gpg
gpg --list-secret-keys

ISP y u no DNSSEC?

Unbound is a secure, lightweight and high performance validating, recursive, and caching DNS resolver. It performs DNSSEC validation and it is also really easy to configure. Although it cannot act as a full fledged authoritative server, you may take a look at NSD, which is, on the contrary, authoritative only. For a nice tutorial about unbound configuration, see this post.

Today I discovered local_unbound on FreeBSD. This script generates a configuration suitable for running unbound as a forwarding resolver. It also uses resolveconf to update the list of forwarders. This means that it is automatically configured with the name servers provided by the DHCP offer.

By default, unbound is configured to withheld the reply from the client when the validation fails. Unfortunately most of the name servers provided by home routers and ISPs do not support DNSSEC. Even worse, some ISPs redirect all queries to their own name servers which of course do not support DNSSEC either. You can check if it is the case with this command:

dig @8.8.8.8 +short test.dnssec-or-not.net TXT

As Google public DNS support DNSSEC. If it does not show you anything, it is OK. If instead it says something like: “Nope!  DO bit not set in your query” then your query has been forwarded to a server which is not DNSSEC capable.

Because of this, you often end up with a nice caching forwarding resolver which does not seem to resolve anything (at least on the client side) because of bogus validations.

You have two solutions to overcome this. First you can force your forwarders to DNSSEC capable servers such as Google public DNS (8.8.8.8 / 8.8.4.4). However, as mentioned above this does not work when your ISP redirect all queries to its own server. The second solution is to configure unbound into permissive mode (/etc/unbound/unbound.conf):

server:
    (...)
    val-permissive-mode: yes

In that case, replies to queries that did not pass validation are not longer withheld from the client but the Authenticated Data bit is not set in the reply.

Configure audio devices on VirtualBox

I recently installed a new Debian guest on a FreeBSD host with VirtualBox. On this VM I need to be able to play/record sound from my laptop (Skype). To do so I configure the guest with an Intel HDA soundcard using the OSS audio driver. However I want to use the internal mic (/dev/dsp1.0) which is different than the default unit (/dev/dsp). So I had basically two options. Either I could record my voice but could not hear anything or I could hear everything but could not record my voice.

But there is a solution! I digged in the VirtualBox source code, and there are actually some options for the OSS driver (and other audio drivers as well). The two options of interests are:

  • DACDev: Path to the DAC (output/playback) device.
  • ADCDev: Path to the ADC (input/record) device.

You can use VBoxManage to configure these options. Assuming that the name of the VM is MyVM with an Intel HDA card and the OSS driver, use this command:

$ VBoxManage setextradata MyVM "VBoxInternal/Devices/hda/0/LUN#0/Config/Audio/OSS/ADCDev" /dev/dsp1.0

You can also directly edit the vbox file for this VM:

MyVM.vbox:

<ExtraData>
  ...
  <ExtraDataItem name="VBoxInternal/Devices/hda/0/LUN#0/Config/Audio/OSS/ADCDev" value="/dev/dsp1.0"/>
</ExtraData>

Note if when starting the VM you receive the message The attached driver configuration is missing the 'Driver' attribute (VERR_PDM_CFG_MISSING_DRIVER_NAME). This is probably because the sound device you selected for this VM is not the same as specified in the extra data. The above example is for Intel HD Audio (hda). If you need ICH AC97, replace hda by ichac97 in the example above.

FreeBSD suspend and ttys.

I use FreeBSD 11-CURRENT on a Thinkpad X201 laptop. I said in a preceding post (Hello FreeBSD!) that although the Intel KMS driver works, I did not have access to the ttys and also that suspend and resume did not work.

This is no longer the case! I rebuiIt world two days ago (r274088) and I now have access to the ttys, and suspend/resume also works without any apparent problem (thanks to KMS/VT (NewCons) I guess). FreeBSD 11 is going to be great!

Fingerprint reader on FreeBSD

This solution is very similar to the way fingerprint readers work under Linux. So if you already configured your fingerprint reader on Linux chances are you will find these instructions familiar. However keep in mind than these instructions are for FreeBSD. In particular I did this on 11-CURRENT but it should also work on 10-RELEASE.

The libfprint library provides the support for the fingerprint readers. In particular it provides drivers for some fingerprint readers (you can find the list on their website), and an API to manipulate the device including the code to enroll/match a fingerprint. This library uses libusb-1.0 for communication with the device. FreeBSD 8 and above already include a reimplementation of this library. This also means that you do not have to enable the device kernel-side. As long as libfprint supports the device and you have access to it (/dev/ugen*), it should work.

The pam_fprint module allows you to use your fingerprint reader for authentication. When correctly configured, you are asked to swipe your fingerprint across the reader instead of a password prompt. This module relies on libfprint to access the device and reads the fingerprints from your home directory. However the sync mechanism used by this module can pose problems when multiple applications try to access the device at the same time.

To address this problem the fprintd daemon offers libfprint functionality via D-Bus. In the same way as pam_fprint, this daemon also provides a PAM module, pam_fprintd, for authentication. This module stores the fingerprints into the /var/lib/fprint directory with permission 700. In other words, this directory is not readable/browsable by other users. Also the fprintd daemon is launched by D-Bus with root permissions. Since it is this daemon in particular that accesses the device, you do not have to change the permissions for your user to access it.

In this article I will present you both method, the direct direct method through libfprint (pam_fprint) and the D-Bus method through fprintd (pam_fprintd). However I would strongly recommend the D-Bus method as it requires less configuration, it is more reliable and also more secure to my opinion.

Note that an application must use PAM in order to authenticate with the fingerprint reader. Some ports, for example xscreensaver, disable PAM authentication by default. So you need to recompile it with PAM enabled as described in a previous article.

Direct method

First you need to install the following ports:

  • security/libfprint
  • security/pam_fprint

You may also install security/fprint_demo which will come up handy to test that your fingerprint reader works correctly with libfprint.

Then you need to ensure that the device is accessible by the users. To do so, we first identify the usb port the fingerprint reader is attached to. Then we create a special group for the users that can access the device. After that we create a devfs rule to change the group and permission of the device’s usb port.

First we identify the usb port of the fingerprint reader:

# usbconfig
...
ugen0.3: <Biometric Coprocessor UPEK> at usbus0, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (100mA)
...

Now we know it’s on ugen0.3. It’s time to add a special group for this kind of device:

# pw groupadd fprint
# pw groupmod -m user1,user2,user3

In this case, user1, user2 and user3 will have access to the fingerprint reader and so will be authenticated with their fingerprint. We can tell devfs to change the permission and group of ugen0.3 to fingerprint and 660. Note that /dev/ugen0.3 is a symlink to /dev/usb/0.3.0 so we change the group and permission of that file too:

/etc/rc.conf:
devfs_system_ruleset="localrules"

/etc/devfs.rules:
[localrules=10]
add path 'ugen0.3' mode 0660 group fprint
add path 'usb/0.3.0' mode 0660 group fprint

Now we take the changes into account and test that the device is accessible for say user1. However to make sure that the changes are enabled on all active session, you may as well just reboot your system:

# service devfs restart
# su user1
$ usbconfig
ugen0.3: <Biometric Coprocessor UPEK> at usbus0, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (100mA)

If you installed fprint_demo you can also test that the fingerprint reader works via libfprint with this command. If it works, it will show the device, in my case “UPEK Eikon 2”, say it is ready to use and propose to enroll your fingerprints.

Note this solution is not ideal since the device may not always be attached to the same port. A better solution would be a devd rule that matches the device more precisely.with vendor/product id. I tried to do so but I’m still fairly new to devd and probably got it wrong. Since I found another method that does not involve changing the permissions, I did not look into this. If someone knows better he’s welcome to post his solution below.

Now that we have access to the device, we can tell PAM to authenticate the users with the fingerprint reader when possible. PAM looks for its modules in /usr/lib. However pam_fprint installs itself in /usr/local/lib. To avoid absolute path in the PAM configuration, I create the following symlink /usr/lib/pam_fprint.so -> /usr/local/lib/pam_fprint.so. It’s time to configure PAM. Most services (slim, xscreensaver, …) will refer to /etc/pam.d/system for their PAM configuration, so we only configure this one. We add the pam_fprint module as sufficient to authenticate the user, just before pam_unix (password authentication):

/etc/pam.d/system:
...
# auth
auth    sufficient   pam_fprint.so
auth    required     pam_unix.so     no_warn try_first_pass nullok local_pass
...

Now we can enroll our fingerprints. You need to do this for each user that will authenticate with the fingerprint reader:

$ pam_fprint_enroll
This program will enroll your finger, unconditionally overwriting any selected print that was enrolled previously. If you want to continue, press enter, otherwise hit Ctrl+C

Found device claimed by UPEK Eikon 2 driver
Opened device. It's now time to enroll your finger.

You will need to successfully scan your Right Index Finger 5 times to complete the process.

Scan your finger now.
Enroll stage passed. Yay!

...

Scan your finger now.
Enroll complete!
Enrollment completed!

Finally we can test if everything works correctly. In this case we use sudo to authenticate with our own user:

$ sudo -i
Swipe your right index finger across the fingerprint reader
#

If you can do this, it works.

D-Bus method

In this method we use the fprintd deamon to access libfprint functionality via D-Bus. To my opinion, this method is simpler, more reliable and more secure than the direct method presented above. The only problems are that the D-Bus daemon is harder to understand and thus debug, also if you have a problem with D-Bus, you are stuck.

First you need to install the following ports:

  • security/libfprint
  • security/fprintd

You do not have to ensure that the device is accessible by the users as the fprintd daemon is launched by D-Bus with root permissions. The only thing we have to do now is to tell PAM to authenticate the users with the fingerprint reader when possible. PAM looks for its modules in /usr/lib. However fprintd installs the pam_fprintd module in /usr/local/lib. To avoid absolute path in the PAM configuration, I create the following symlink /usr/lib/pam_fprintd.so -> /usr/local/lib/pam_fprintd.so. It’s time to configure PAM. Most services (slim, xscreensaver, …) will refer to /etc/pam.d/system for their PAM configuration, so we only configure this one. We add the pam_fprintd module as sufficient to authenticate the user, just before pam_unix (password authentication):

/etc/pam.d/system:
...
# auth
auth    sufficient   pam_fprintd.so
auth    required     pam_unix.so     no_warn try_first_pass nullok local_pass
...

Now we can enroll our fingerprints. You need to do this for each user that will authenticate with the fingerprint reader:

$ fprintd-enroll
Using device /net/reactivated/Fprint/Device/0
Enrolling right-index-finger finger.
Enroll result: enroll-stage-passed
...
Enroll result: enroll-completed

Finally we can test if everything works correctly. In this case we use sudo to authenticate with our own user:

$ sudo -i
Swipe your right index finger across the fingerprint reader
#

If you can do this, it works.

Enroll failed with error -22

I use pam_fprint on my laptop. Somehow my fingerprints disappeared and I could not enroll them back with pam_fprint_enroll. I had this error message:

Enroll failed with error -22

To fix this I switched from pam_fprint to fprintd which uses D-Bus. I also had to change /etc/pam.d/system:

-auth    sufficient   pam_fprint.so
+auth    sufficient   pam_fprintd.so

Then I could enroll my fingerprints with fprint-enroll.

Fingerprint and XScreenSaver

Today I configured the fingerprint reader on my Thinkpad X201. I will not describe the process of configuring the fingerprint reader on FreeBSD as it has been documented many times on the Internet. Though if you want I may describe the process in a separate article, just tell me.

After the configuration, everybody from the display manager to su asked to authenticate with the reader. So it was fine. Except for xscreensaver which still confined itself password authentification. There were no configuration for xscreensaver in /etc/pam.d so I initially thought that it fell back on the “other” configuration. Therefore I added one for xscreensaver but it didn’t change anything.

However I installed xscreensaver as a package, and not from the ports. It turns out that the xscreensaver package does not enable the PAM option. You can check that via pkg info xscreensaver or use ldd /usr/local/bin/xscreensaver to check if xscreensaver is effectively linked with libpam.so.

So you can reinstall xscreensaver from the ports and configure it with the PAM option enabled. However I still wonder why they disabled this by default. I still have a problem with PAM which sometime does not detect the reader and cannot even authenticate with pam_unix. I don’t know what is the cause of this, but I’ll find out.