OpenBSD Journal

j2k25 hackathon report from kn@: installer, low battery, and more

Contributed by rueda on from the want TLS with that ramen? dept.

Fresh from the recently concluded j2k25 hackathon comes this report from Klemens Nanni (kn@), who writes:

New country, lots of ramen, friends and new folks - heck, yes!

Having missed the last four (our five?, hard to tell…) hackathons, j2k25 aligned just right to finish our holidays with beautiful sights and culinary delights between streaks of hacking, leaving all else aside for a solid week - it was refreshing retreat and sparked plans to make another, even longer trip through Japan!

This time, I brought a few unfished and/or unanswered diffs, but also specifically wanted to look into unfamiliar code, now that folks were around to ask for advice and discuss with.


First, the installer and rc(8) were due for cleanup: common code for randomness seed files used by bootloaders and rc seemed unnecessarily different, so I synced their logic, style and comments wrt. subtle, yet important details around the sticky(8) bit:

Basically, as now clarified in chmod(2), +t on files is no effect in the kernel, so bootblocks have long since repurposed this ancient semantic to signal entropy bits, i.e. whether they had read the same /etc/random.seed off disk and marked it with +t a second time, without the installer or rc(8) updating it during upgradeor boot/shutdown.

	NOTE: random seed is being reused. 

If you ever saw this, it meant that the tiny shim of FFS write support expected a fresh file, but saw the (same) old sticky one again.

Afterwards came a tweak to the 'Location of sets?' question to opt "http" and "nfs" choices behind functional network for install77.* to correctly default to [disk] or [cd0] instead of [http] in offline environments without interfaces; this seems small, but I appreciate each step towards more boring <Enter> (or less autoinstall(8) lines) doing the right thing.


Unless there's a dedicated ports hackathon, I try to stay away from that tree with its time consuming chores, but with new devel/abseil-cpp (important dependency) and net/tdesktop in the queue, moving fast with lots of other changes actually saves time as diffs don't need retesting over time.

So I dedicated a day to a) the new abseil LTS as well as building and syncing almost every consumer and b) non-trivial surgery in net/tdlib to update and split the port into net/tdlib/td{lib,e2e} where tde2e exposes private API - effectively a completely different library used by Telegram Desktop. This was the result of prior discussions with other systems' package maintainers plus great feedback from tb@ and sthen@ as usual.

Next, just when I thought this ports effort concluded, users reported Telegram group calls being broken (not new) on OpenBSD… I hadn't even used that feature before, but decided to poke further with my head still /p/pobj/ trying to make sense of all this alien code.

A tour through net/tg_owt, security/libsrtp and tdesktop plus hints from folks on FreeBSD and Linux led to the theory that something in LibreSSL DTLS goes wrong - debug logs only prompted more questions:

	(openssl_adapter.cc:820): read_alert fatal internal error SSL negotiation finished successfully

I looked a little further, but decided to not waste more hackathon time on what is likely no easy fix.

Having rsadowski@ around, we discussed issues I have been seeing in the KDE Plasma desktop: a crash whilst typing in the program search and a log file filling itself endlessly by tracking file changes to itself (can't make this up). I just showed it to him, we tried a few things until he nailed it down and fixed both by disabling unhelpful bits and providing a convenient kde-plasma update to crank resources.

Having returned the favour with reviews for KDE Plasma packages^W^W^W even slower bulk builds, I could finally return to base!


Back in 2023 my notebook ran out of battery once too often while working focused; knowing battery life isn't great, I've been using apmd(8)'s -z/-Z to automatically suspend/hibernate below a certain battery threshold and it saved me every time, but what I actually wanted was some kind of warning *before* that feature kicked in, so I could plug into AC and continue work without being forcefully interrupted, losing network, etc.

Thus I hacked together [-w percent] plus a /etc/apm/warnlow hook that would a) issue warning logs whilst on battery *and* below the given threshold, but more importantly also run the script every time it triggered. It worked great for me, so I mailed it out, but never got a response and quickly forgot about it after becoming used to it.

It wasn't until a chat with tobhe@ about various quirks around (ARM) notebooks, that this came up and I dusted off the diff in my tree to mail a new version around, now with better docs and someone interested in it.

This time jmc@ caught wind as well, provided valuable feedback and a fresh look onto the manual. Finally, OKs from Tobias and Jason for code and docs got the warnlow feature in together with brief examples (precisely what I use) and changelist(5) for script change detection.


Sitting next to job@, we kept disussing the old and new watch(1) [Not linked to build at time of writing - Ed.] tool he was working on: what it should (not) do, which aspects should be similiar to existing base tools like top(1) or systat(1), what to rip our and/or keep in sync with other watch tools; small steps, quick diffs and OKs and demos to show, all without delay across timezones or throug mail is exactly what hackathons are for!


Last comes a chapter about chroot(2), its relation with daemon(3) in some base programs, code being copied around and old, yet novel ideas that seem to be a better solution… or not.

An unrelated question made me look at tftpd(8) weeks ago. Back then I noticed its local rdaemon() function, studied it further, read CVS history, but it felt like I was missing the crucial point. Indeed, jca@, who authored this code in 2016, had to explain the obvious to me after I mailed wrong diff around, asking for clues.

The gist is: chroot(2) before daemon(3) causes the latter to fail closing standard streams (/dev/null is no longer around). The "fun" bit is that daemon(3) won't report this in any way and still return "success", which makes you believe everything works as expected unless you peek through ktrace(1) or inspect a program with fstat(1).

Thus rdaemon() came to be a local copy taking a preopened file descriptor to dup2(2) so programs can 1) open /dev/null, 2) chroot(/somewhere) and 3) rdaemon(nullfd) to avoid the pitfall of silently leaving 0, 1, 2 open after forking into the background.

millert@ had already pointed out FreeBSD's daemonfd(3) being exactly what daemons like tftpd(8) need, where chroot(2) guarantees filesystem containment inside the server's directory root without the need for ugly, brittle string checks around "../" and what not to mitigate path traversal attacks.

So I ported daemonfd(3), made tftpd(8) use that instead of rdaemon(), mailed the combined diff as PoC and enthusiastically set out to clean up other base daemons which copied over rdaemon(), thinking it'd be a few similiar tweaks, but I noticed how none out of the six other programs actually relied on chroot() for the same path-rewrite semantics like tftpd does. One didn't even use chroot(), but still had rdaemon(). Some even lack filesystem access under pledge(2) without "[rwc]path", questioning, imho, the need to move / for the process. Another one unable to pledge() now employs the unveil("/", "") idiom.

Details and discussions about this with a handful of people, mostly deraadt@ though, spread across many mails, diffs and conversations often going over related ideas. I learned a lot about how unveil(2) works internally, how it is massively more complex than the simple and battle-tested chroot() concept and that they're not interchangable as means to security programs. pledge() a lot simpler and easier to reason about, but even there seem to be arguments for having both chroot() and pledge() instead of relying on the latter without *path promises alone.


Out of all this also came the wish for a new mechanism to contain file path operations on a finer per-directory level, without the need for privileged operations, that would simple enough to implement and trust and could be used where chroot(2) could not.

I had recently read about the openat(2) O_BENEATH flag in FreeBSD and Linux, which initially sounded like a viable solution, but talking with Theo made it pretty clear, that having to opt into a mechanism for every single open of a file is a kind of backwards semantic, considering that an attribute of the containing directory itself seems more natural and less error-prone rather than setting a flag for each path that's to be resolved inside of that and never elsewhere.

More on that is better read in Theo's mail on tech@ titled "openat(2) is mostly useless, sadly" than repeated here. I'll just add that this was^Wstill is a fun ride for me through namei(9) plus friends and promising use cases for such a potential new O_BELOW flag.


Many thanks to the OpenBSD foundation and Yasuoka for enabling this wonderful trip and awesome hackathon!


Credits

Copyright © - Daniel Hartmeier. All rights reserved. Articles and comments are copyright their respective authors, submission implies license to publish on this web site. Contents of the archive prior to as well as images and HTML templates were copied from the fabulous original deadly.org with Jose's and Jim's kind permission. This journal runs as CGI with httpd(8) on OpenBSD, the source code is BSD licensed. undeadly \Un*dead"ly\, a. Not subject to death; immortal. [Obs.]